Тернарный оператор и оператор запятая

Название «тернарный» произошло от латинского ternarius – тройной. Оператор принимает три аргумента. Если первый аргумент истина, то возвращается второй аргумент, если ложь, то возвращается третий.

Синтаксис оператора

<условие> ? <аргумент 2> : <аргумент 3>

Например, пользователь вводит два числа. Присвоить переменной значение наименьшего.

#define _CRT_SECURE_NO_WARNINGS
#include <conio.h>
#include <stdio.h>

void main() {
    int a, b, c;

    scanf("%d %d", &a, &b);

    c = (a > b) ? b : a;

    printf("%d", c);
    _getch();
}

Другой пример: вывести модуль числа

#define _CRT_SECURE_NO_WARNINGS
#include <conio.h>
#include <stdio.h>

void main() {
    int a;

    scanf("%d", &a);

    printf("%d", a > 0 ? a : -a);
    _getch();
}

Здесь тернарный оператор используется вместо аргумента функции printf.

Оператор ? может быть вложенным. Например, найдём среднее из трёх чисел:

#include <conio.h>
#include <stdio.h>

void main() {
    int a = 1;
    int b = 2;
    int c = 3;
    int mid;

    mid = (a > b) ? (a > c) ? (c > b) ? c : b : a : (b > c) ? (c > a) ? c : a : b;

    printf("%d", mid);
    _getch();
}

Видно, что в этом примере тернарный оператор становится нечитаемым. Поэтому не используйте вложенные тернарные операторы и заменяйте их на обычные ветвления

#include <conio.h>
#include <stdio.h>

void main() {
    int a = 1;
    int b = 2;
    int c = 3;
    int mid;

    if (a > b) {
        if (a > c) {
            if (c > b) {
                mid = c;
            } else {
                mid = b;
            }
        } else {
            mid = a;
        }
    } else {
        if (b > c) {
            if (c > a) {
                mid = c;
            } else {
                mid = a;
            }
        } else {
            mid = b;
        }
    }

    printf("%d", mid);
    _getch();
}

Для тернарного оператора определён порядок выполнения. Все значение и побочные эфекты, связанные с первым выражением реализуются перед тем, как будут реализованы значения и побочные эффекты для второго и третьего выражений.

#include <conio.h>
#include <stdio.h>

void main() {
    int x = 0;

    //Выведет 200, так как ++ постфиксный
    printf("%d\n", x++ ? 100: 200);
    //Выведет 1, так как после проверки x был увеличен
    printf("%d\n", x);

    x = 0;
    //Выведет 100, так как сначала будет увеличен x
    printf("%d\n", ++x ? 100 : 200);
    printf("%d\n", x);

    x = 0;
    printf("%d\n", ++x ? x++ : x++);
    //Выведет 2, так как будет выполнено первое условие
    //и x будет инкрементирован повторно
    printf("%d\n", x);

    _getch();
}

Оператор запятая.

Оператор , принимает два аргумента, выполняет первый, после этого выполняет второй и возвращает его значение. Например:

#include <conio.h>
#include <stdio.h>

void main() {
    int a, b, c;

    c = (a = 3, b = 4);
    printf("%d", c);

    _getch();
}

В данном примере c будет равно 4. Выражение

(a = 3, b = 4)

вернёт 4. Очевидно, что если убрать скобки, то c станет равным 3, так как запятая в этом случае будет разделять c = a = 3 и b = 4, с присвоится значение 3, а 4 будет возвращено оператором запятая, но потеряется. То есть выражение

c = a = 3, b = 4;

эквивалентно

(c = a = 3), (b = 4);

Если написать

c = (a = 3, b = 4, d = 5);

то будет возвращено значение 5.

В выражении

#include <conio.h>
#include <stdio.h>

void main() {
    int c;

    c = 1, 2, 3, 4, 5;
    printf("%d", c);

    _getch();
}

с будет равно 1, так как этот код эквивалентен такому

((((c = 1), 2), 3), 4), 5;

Такое поведение оператора запятая часто приводит к следующей ошибке. Вместо десятичной точки

a = 0.5;

пишут

a = 0,5;

Таким образом, переменная a будет иметь значение 0.

Для оператора , гарантированно выполнение по порядку, слева направо. Оператор запятая поэтому используется в условиях, когда нужно выполнить несколько действий или получить значение. Например

#include <conio.h>
#include <stdio.h>

int foo(int a) {
    return a % 5 + a*a;
}

void main() {
    int i = 0;
    int mod;

    while (mod = foo(i), mod < 100) {
        printf("%d ", i);
        i++;
    }

    _getch();
}

Часто запятая используется в цикле for, так как позволяет задавать несколько значений или выполнять несколько операций за раз. Пример – переворачивание массива.

#include <conio.h>
#include <stdio.h>

#define SIZE 10

void main() {
    int arr[SIZE] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int i, j, tmp;

    for (i = 0, j = SIZE - 1; i < SIZE / 2; i++, j--) {
        tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }

    for (i = 0; i < SIZE; i++) {
        printf("%d ", arr[i]);
    }

    _getch();
}